package com.dodayum.utils;

import com.dodayum.entity.JobBean;
import com.dodayum.entity.JobInfo;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 定时任务的动态调度
 * 使用方法，线注入Scheduler，然后直接调用就好了
 */
@Service
public class JobUtils {

    @Resource
    private Scheduler scheduler;

    /**
     * 生成任务
     *
     * @param jobBean 任务bean
     */
    public Date createJob(JobBean jobBean) throws SchedulerException {
        JobBuilder jobBuilder = JobBuilder
                .newJob(jobBean.getJobClass())
                .storeDurably() // 开启持久化
                .withIdentity(jobBean.getJobName(), jobBean.getJobGroup()) // 唯一标识
                .withDescription(jobBean.getDescription());// 备注
        jobBean.getData().forEach(jobBuilder::usingJobData);
        JobDetail jobDetail = jobBuilder.build();

        Trigger trigger = TriggerBuilder
                .newTrigger()
                .forJob(jobDetail) // 进行关联
                .withIdentity(jobBean.getJobName() + "_trigger", jobBean.getJobGroup() + "_trigger") // 唯一标识
                .withSchedule(CronScheduleBuilder.cronSchedule(jobBean.getCron())) // 时间规则
                .build();
        return scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 暂停任务
     *
     * @param jobBean 任务名
     */
    public void pauseJob(JobBean jobBean) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobBean.getJobName(), jobBean.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复任务
     *
     * @param jobBean 任务名
     */
    public void resumeJob(JobBean jobBean) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobBean.getJobName(), jobBean.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除任务
     *
     * @param jobBean 任务名
     */
    public boolean deleteJob(JobBean jobBean) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobBean.getJobName(), jobBean.getJobGroup());
        return scheduler.deleteJob(jobKey);
    }

    /**
     * 修改任务
     *
     * @param jobBean 任务bean
     */
    public Date modifyJon(JobBean jobBean) throws SchedulerException {
        // 获取任务唯一标识
        TriggerKey triggerKey = TriggerKey.triggerKey(jobBean.getJobName() + "_trigger");
        // 通过唯一标识获取旧的触发器对象
        CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        // 实用新的cron表达式构建新的触发器
        String newCron = jobBean.getCron();
        CronTrigger newTrigger = oldTrigger.getTriggerBuilder()
                .withSchedule(CronScheduleBuilder
                        .cronSchedule(newCron)
                        .withMisfireHandlingInstructionDoNothing()
                )
                .build();
        // 调度器更新任务的触发器
        return scheduler.rescheduleJob(triggerKey, newTrigger);
    }

    /**
     * 查询所有
     *
     * @return List<JobInfo>
     * @throws SchedulerException 远程计划有关的
     */
    public List<JobInfo> queryAll() throws SchedulerException {
        List<JobInfo> response = new ArrayList<>();
        Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup());
        for (TriggerKey triggerKey : triggerKeys) {
            Trigger trigger = scheduler.getTrigger(triggerKey);
            JobKey jobKey = trigger.getJobKey();
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            String status = null;
            switch (triggerState) {
                case NONE:
                    /*
                    这个状态可能表示Trigger或Job尚未被初始化或没有设置状态。在实际应用中，您可能不太会看到这个状态，因为一旦Trigger被创建并加入到Scheduler中，它通常会被赋予一个明确的状态。
                     */
                    System.out.println("NONE");
                    status = "执行中";
                    break;
                case NORMAL:
                    /*
                    对于Trigger，NORMAL状态通常表示它正在等待下一次触发时间到来。一旦到达预定的触发时间，Trigger将尝试执行其关联的Job。
                     */
                    System.out.println("等待触发");
                    status = "等待触发";
                    break;
                case PAUSED:
                    /*
                    当Trigger或Scheduler被暂停时，它们会进入PAUSED状态。在PAUSED状态下，Trigger不会触发任何Job，即使它们的预定触发时间已经到达。要恢复执行，您需要取消暂停状态。
                     */
                    System.out.println("暂停");
                    status = "暂停";
                    break;
                case COMPLETE:
                    /*
                    对于Trigger，COMPLETE状态表示它已成功触发其关联的Job，并且没有更多的触发计划。这通常发生在Trigger的所有触发次数都已经用完，或者Job执行完成并且没有更多的触发条件满足时。
                     */
                    System.out.println("COMPLETE");
                    status = "无关联";
                    break;
                case ERROR:
                    /*
                    ERROR状态表示在Trigger或Job的执行过程中发生了错误。这可能是由于多种原因造成的，例如Job实现中的异常、Trigger配置错误或Scheduler的内部错误。当遇到ERROR状态时，您应该检查相关的日志和异常信息以确定问题的原因。
                     */
                    System.out.println("ERROR");
                    status = "报错";
                    break;
                case BLOCKED:
                    /*
                    BLOCKED状态通常与并发执行有关。当Job被配置为不允许并发执行，并且前一个Job实例仍在运行时，新的触发请求会导致Trigger进入BLOCKED状态。这意味着尽管Trigger的触发时间已经到达，但由于Job的并发限制，它不能立即执行。
                     */
                    System.out.println("BLOCKED");
                    status = "并发限制";
                    break;
                default:
                    System.out.println("未知");
            }

            JobInfo jobInfo = new JobInfo();
            jobInfo.setJobName(jobDetail.getKey().getName());
            jobInfo.setJobGroup(jobDetail.getKey().getGroup());
            jobInfo.setStatus(status);
            jobInfo.setNetTime(trigger.getNextFireTime());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cron = cronTrigger.getCronExpression();
                jobInfo.setCron(cron);
            }
            response.add(jobInfo);
        }
        return response;
    }
}
